#ifndef CONSTRUCTORA_H
#define CONSTRUCTORA_H

#include <iostream>
#include "defines.h"
#include "lista.h"
#include "conjunto.h"
#include "arreglo_redimensionable.h"
#include "multiconj_stock.h"
#include "multiconj_materiales.h"
#include "catalogo.h"
#include "pendientes.h"

class constructora{
public:
	constructora(const Catalogo& c);
	~constructora();
	const Catalogo& catalogo() const;
	const multiconj_stock& stock() const;
	const Multiconj_materiales& deuda() const;
	const Pendientes& pendientes() const;
	void construir(vivienda v);
	void comprar(material m, nat n);
	void donar();

private:
	bool puedoConstruir(vivienda v) const;
	bool puedoConstruirArray(vivienda v,const int* a) const;
	void construirVivienda(vivienda v);
	void construirViviendaArray(vivienda v, int* a);	


	multiconj_stock _stock;
	Multiconj_materiales _deuda;
	Pendientes _pendientes;
	const Catalogo& _catalogo;
	
};

constructora::constructora(const Catalogo& c):
	_deuda(Multiconj_materiales()),
	_catalogo(c),
	_stock(c.cantMateriales()+1),
	_pendientes(c, c.cantMateriales()){}

constructora::~constructora(){}
       
const Catalogo& constructora::catalogo() const{
	return _catalogo;
}

const multiconj_stock& constructora::stock() const{
	return _stock;
}

const Multiconj_materiales& constructora::deuda() const{
	return _deuda;
}

const Pendientes& constructora::pendientes() const{
	return _pendientes;
}

void constructora::construir(vivienda v){
	if(puedoConstruir(v)){
		construirVivienda(v);
	}
	else{
		_pendientes.encolar(v);
	}
}

void constructora::comprar(material m,nat n){
	assert(m<_stock.cantMateriales());
	nat meQueda = n - min(n,_deuda.cardinal(m));
	_deuda.sacar(m,n);
	if(meQueda > 0 || _deuda.vacio()){//O(cantMateriales)
		nat cant = _stock.cantMateriales();
		int* a = new int [cant];
		for(int i=0;i<cant;i++){
			a[i]=_stock.cardinal(i);
		}
		a[m]=a[m]+meQueda;
	//podemos hacer el while porque si no pide deuda es porque sigue alcanzando el material comprado y seria lo mismo que recorrer los pendientes segun el material (cumple la complejidad)
		while(_deuda.vacio() && _pendientes.hayPendiente()){//O(cant_viviendas_con_m)
			vivienda v = _pendientes.primera();//vivienda pendiente con mayor prioridad
			construirViviendaArray(v,a);
			Multiconj_materiales matVivienda = _catalogo.materiales(v);//materiales que necesita la vivienda que construi
			if(matVivienda.esta(m)){//si necesita el material que compre, veo que cantidad y actualizo meQueda
				nat cant = matVivienda.cardinal(m);
				meQueda = meQueda - min(meQueda, cant);
			}
			_pendientes.desencolar();
		}
		if(meQueda > 0){//O(cant_viviendas_con_m)
//si quedo algo del material que compre recorro las pendientes que necesitan ese material
			Pendientes::iterPendientes it_p = _pendientes.crearIterPendientes(m);
			while(it_p.tieneProximo()){
				vivienda v = it_p.actual();
				if(puedoConstruirArray(v,a)){
					construirViviendaArray(v,a);
					it_p.eliminar();
				}else{
					it_p.avanzar();
				}
			}
		}
		_stock.actualizarStock(a);//O(cantMateriales)
		delete [] a;
	}
}

void constructora::donar(){
	material m = _stock.elQueMasHay();
	nat n = _stock.cardinal(m)/5;
	_stock.sacar(m,n);
}

bool constructora::puedoConstruir(vivienda v) const {
	Multiconj_materiales::IterMateriales it_m = _catalogo.crearIterMateriales(v);//itero sobre los materiales de v
	bool res = true;
	while(res && it_m.tieneProximo()){
		material mat = it_m.avanzar();
		nat loQueNecesito = _catalogo.materiales(v).cardinal(mat);
		if(_stock.cardinal(mat)<loQueNecesito && !_deuda.vacio()){
			res = false;
		}
	}
	return res;
}

bool constructora::puedoConstruirArray(vivienda v, const int* a) const {
	Multiconj_materiales::IterMateriales it_m = _catalogo.crearIterMateriales(v);//itero sobre los materiales de v
	bool res = true;
	while(res && it_m.tieneProximo()){
		material mat = it_m.avanzar();
		nat loQueNecesito = _catalogo.materiales(v).cardinal(mat);
		if(a[mat]<loQueNecesito && !_deuda.vacio()){
			res = false;
		}
	}
	return res;
}

void constructora::construirVivienda(vivienda v) {
	Multiconj_materiales::IterMateriales it_m = _catalogo.crearIterMateriales(v);//itero sobre los materiales de v
	while(it_m.tieneProximo()){
		material mat = it_m.avanzar();
		nat loQueNecesito = _catalogo.materiales(v).cardinal(mat);
		nat loQueTengo = _stock.cardinal(mat);
		if(loQueTengo < loQueNecesito){
			_deuda.agregar(mat, loQueNecesito-loQueTengo);
			_stock.sacar(mat, loQueTengo);
		}else{
			_stock.sacar(mat, loQueNecesito);
		}
	}
}

void constructora::construirViviendaArray(vivienda v, int* a) {
	Multiconj_materiales::IterMateriales it_m = _catalogo.crearIterMateriales(v);
	while(it_m.tieneProximo()){
		material mat = it_m.avanzar();
		nat loQueNecesito = _catalogo.materiales(v).cardinal(mat);
		nat loQueTengo = a[mat];
		if(loQueTengo < loQueNecesito){
			_deuda.agregar(mat, loQueNecesito-loQueTengo);
			a[mat] = a[mat] - loQueTengo;
		}else{
			a[mat] = a[mat] -loQueNecesito;
		}
	}
}

#endif
